{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "16bb4bc3",
   "metadata": {},
   "source": [
    "## shell\n",
    "\n",
    "Shell命令是操作系统中用于在命令行界面（CLI）中执行各种操作的工具。它们可以用于文件和目录管理（如创建、删除、移动和复制）、查看和编辑文本文件、管理系统权限和所有权、监控系统资源、进行网络通信和远程管理，以及安装和更新软件包等。掌握Shell命令可以极大地提高工作效率和系统的管理能力。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "50b5ef2f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Executing command:\n",
      " [\"echo 'hello world'\"]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\a4154\\AppData\\Roaming\\Python\\Python310\\site-packages\\langchain_community\\tools\\shell\\tool.py:32: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"'hello world'\\r\\n\""
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.tools import ShellTool\n",
    "\n",
    "shell_tool = ShellTool()\n",
    "\n",
    "shell_tool.run({\"commands\": [\"echo 'hello world'\"]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a424c0ca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Executing command:\n",
      " ['start chrome']\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\a4154\\AppData\\Roaming\\Python\\Python310\\site-packages\\langchain_community\\tools\\shell\\tool.py:32: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "shell_tool.run({\"commands\": [\"start chrome\"]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "239f8449",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Executing command:\n",
      " ['start chrome.exe http://www.baidu.com/s?wd=仙剑四']\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\a4154\\AppData\\Roaming\\Python\\Python310\\site-packages\\langchain_community\\tools\\shell\\tool.py:32: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "shell_tool.run({\"commands\": [\"start chrome.exe http://www.baidu.com/s?wd=仙剑四\"]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "123daf41",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Executing command:\n",
      " ['mkdir newtxt']\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\a4154\\AppData\\Roaming\\Python\\Python310\\site-packages\\langchain_community\\tools\\shell\\tool.py:32: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "shell_tool.run({\"commands\": [\"mkdir newtxt\"]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "369c90c7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Executing command:\n",
      " ['mkdir 老陈文件夹']\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\a4154\\AppData\\Roaming\\Python\\Python310\\site-packages\\langchain_community\\tools\\shell\\tool.py:32: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "''"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.agents import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def run_shell(command: str) -> int:\n",
    "    \"\"\"在电脑上运行shell的命令操控电脑，例如输入command命令打开浏览器用百度搜索郭德纲，则command命令为 start chrome http://www.baidu.com/s?wd=郭德纲\"\"\"\n",
    "    return shell_tool.run({\"commands\": [command]})\n",
    "\n",
    "\n",
    "run_shell.invoke(\"mkdir 老陈文件夹\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0af14a1",
   "metadata": {},
   "source": [
    "## 加载LLM\n",
    "\n",
    "首先，让我们加载将用于控制代理的语言模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "016d09ea",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI, OpenAI\n",
    "\n",
    "openai_api_key = \"EMPTY\"\n",
    "openai_api_base = \"http://127.0.0.1:1234/v1\"\n",
    "model = ChatOpenAI(\n",
    "    openai_api_key=openai_api_key,\n",
    "    openai_api_base=openai_api_base,\n",
    "    temperature=0.3,\n",
    ")\n",
    "llm = model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "143aa5b4",
   "metadata": {},
   "source": [
    "## 定义工具\n",
    "\n",
    "接下来，让我们定义一些要使用的工具。让我们编写一个非常简单的 Python 函数来计算传入的单词的长度。\n",
    "\n",
    "请注意，这里我们使用的函数文档字符串非常重要。在此处阅读有关为什么会这样的更多信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "fa2269a9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.agents import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def get_word_length(word: str) -> int:\n",
    "    \"\"\"Returns the length of a word.\"\"\"\n",
    "    return len(word)\n",
    "\n",
    "\n",
    "get_word_length.invoke(\"abc\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "18c75770",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'description': '阴', 'temperature': '24'}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import requests\n",
    "\n",
    "@tool\n",
    "def get_weather(location):\n",
    "    \"\"\"根据城市获取天气数据\"\"\"\n",
    "    api_key = \"SKcA5FGgmLvN7faJi\"\n",
    "    url = f\"https://api.seniverse.com/v3/weather/now.json?key={api_key}&location={location}&language=zh-Hans&unit=c\"\n",
    "    response = requests.get(url)\n",
    "    #print(location)\n",
    "    if response.status_code == 200:\n",
    "        data = response.json()\n",
    "        #print(data)\n",
    "        weather = {\n",
    "            'description':data['results'][0][\"now\"][\"text\"],\n",
    "            'temperature':data['results'][0][\"now\"][\"temperature\"]\n",
    "        }\n",
    "        return weather\n",
    "    else:\n",
    "        raise Exception(f\"失败接收天气信息：{response.status_code}\")\n",
    "            \n",
    "get_weather.invoke(\"广州\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "5a36f8df",
   "metadata": {},
   "outputs": [],
   "source": [
    "tools = [get_word_length,get_weather,run_shell]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d3f6ea25",
   "metadata": {},
   "source": [
    "## 创建提示\n",
    "\n",
    "现在让我们创建提示。由于 OpenAI 函数调用针对工具使用进行了微调，因此我们几乎不需要任何关于如何推理或如何输出格式的说明。我们将只有两个输入变量： input 和 agent_scratchpad 。 input 应为包含用户目标的字符串。 agent_scratchpad 应该是包含先前代理工具调用和相应工具输出的消息序列。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "5b49ce6d",
   "metadata": {},
   "outputs": [],
   "source": [
    "promptTemplate = \"\"\"尽可能的帮助用户回答任何问题。\n",
    "\n",
    "您可以使用以下工具来帮忙解决问题，如果已经知道了答案，也可以直接回答：\n",
    "\n",
    "{tools}\n",
    "\n",
    "回复格式说明\n",
    "----------------------------\n",
    "\n",
    "回复我时，请以以下两种格式之一输出回复：\n",
    "\n",
    "选项 1：如果您希望人类使用工具，请使用此选项。\n",
    "采用以下JSON模式格式化的回复内容：\n",
    "\n",
    "```json\n",
    "{{\n",
    "    \"reason\": string, \\\\ 叙述使用工具的原因\n",
    "    \"action\": string, \\\\ 要使用的工具。 必须是 {tool_names} 之一\n",
    "    \"action_input\": string \\\\ 工具的输入\n",
    "}}\n",
    "````\n",
    "\n",
    "选项2：如果您认为你已经有答案或者已经通过使用工具找到了答案，想直接对人类做出反应，请使用此选项。 采用以下JSON模式格式化的回复内容：\n",
    "\n",
    "```json\n",
    "{{\n",
    "  \"action\": \"Final Answer\",\n",
    "  \"answer\": string \\\\最终答复问题的答案放到这里！\n",
    "}}\n",
    "````\n",
    "\n",
    "用户的输入\n",
    "--------------------\n",
    "这是用户的输入（请记住通过单个选项，以JSON模式格式化的回复内容，不要回复其他内容）：\n",
    "\n",
    "{input}\n",
    "\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "d2274f63",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"你是非常强大的助手，你可以使用各种工具来完成人类交给的问题和任务。\",\n",
    "        ),\n",
    "        (\"user\", promptTemplate),\n",
    "        MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "f1c3bfc2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatPromptTemplate(input_variables=['agent_scratchpad', 'input'], input_types={'agent_scratchpad': typing.List[typing.Union[langchain_core.messages.ai.AIMessage, langchain_core.messages.human.HumanMessage, langchain_core.messages.chat.ChatMessage, langchain_core.messages.system.SystemMessage, langchain_core.messages.function.FunctionMessage, langchain_core.messages.tool.ToolMessage]]}, partial_variables={'tools': 'get_word_length: get_word_length(word: str) -> int - Returns the length of a word.\\nget_weather: get_weather(location) - 根据城市获取天气数据\\nrun_shell: run_shell(command: str) -> int - 在电脑上运行shell的命令操控电脑，例如输入command命令打开浏览器用百度搜索郭德纲，则command命令为 start chrome http://www.baidu.com/s?wd=郭德纲', 'tool_names': 'get_word_length, get_weather, run_shell'}, messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], template='你是非常强大的助手，你可以使用各种工具来完成人类交给的问题和任务。')), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input', 'tool_names', 'tools'], template='尽可能的帮助用户回答任何问题。\\n\\n您可以使用以下工具来帮忙解决问题，如果已经知道了答案，也可以直接回答：\\n\\n{tools}\\n\\n回复格式说明\\n----------------------------\\n\\n回复我时，请以以下两种格式之一输出回复：\\n\\n选项 1：如果您希望人类使用工具，请使用此选项。\\n采用以下JSON模式格式化的回复内容：\\n\\n```json\\n{{\\n    \"reason\": string, \\\\ 叙述使用工具的原因\\n    \"action\": string, \\\\ 要使用的工具。 必须是 {tool_names} 之一\\n    \"action_input\": string \\\\ 工具的输入\\n}}\\n````\\n\\n选项2：如果您认为你已经有答案或者已经通过使用工具找到了答案，想直接对人类做出反应，请使用此选项。 采用以下JSON模式格式化的回复内容：\\n\\n```json\\n{{\\n  \"action\": \"Final Answer\",\\n  \"answer\": string \\\\最终答复问题的答案放到这里！\\n}}\\n````\\n\\n用户的输入\\n--------------------\\n这是用户的输入（请记住通过单个选项，以JSON模式格式化的回复内容，不要回复其他内容）：\\n\\n{input}\\n\\n')), MessagesPlaceholder(variable_name='agent_scratchpad')])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.tools.render import  render_text_description\n",
    "\n",
    "prompt = prompt.partial(\n",
    "        tools=render_text_description(list(tools)),\n",
    "        tool_names=\", \".join([t.name for t in tools]),\n",
    ")\n",
    "prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "31468772",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'get_word_length: get_word_length(word: str) -> int - Returns the length of a word.\\nget_weather: get_weather(location) - 根据城市获取天气数据\\nrun_shell: run_shell(command: str) -> int - 在电脑上运行shell的命令操控电脑，例如输入command命令打开浏览器用百度搜索郭德纲，则command命令为 start chrome http://www.baidu.com/s?wd=郭德纲'"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "render_text_description(list(tools))\n",
    "\n",
    "# get_word_length: get_word_length(word: str) -> int - Returns the length of a word.\n",
    "# get_weather: get_weather(location) - 根据城市获取天气数据\n",
    "# searxng_search: searxng_search(query) - 输入搜索内容，使用 SearXNG 进行搜索。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ff6d290",
   "metadata": {},
   "source": [
    "## 将工具绑定到LLM\n",
    "\n",
    "在这种情况下，我们依赖于 OpenAI 工具调用 LLMs，它将工具作为一个单独的参数，并经过专门训练，知道何时调用这些工具。\n",
    "\n",
    "要将我们的工具传递给代理，我们只需要将它们格式化为 OpenAI 工具格式并将它们传递给我们的模型。（通过 bind 对函数进行 -ing，我们确保每次调用模型时都传入它们。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b46c172a",
   "metadata": {},
   "source": [
    "## 创建代理\n",
    "\n",
    "将这些部分放在一起，我们现在可以创建代理。我们将导入最后两个实用程序函数：一个用于格式化中间步骤（代理操作、工具输出对）以输入可发送到模型的消息的组件，以及一个用于将输出消息转换为代理操作/代理完成的组件。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "fe513870",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"TOOL RESPONSE: \\n---------------------\\n{observation}\\n\\nUSER'S INPUT\\n--------------------\\n\\nOkay, so what is the response to my last comment? If using information obtained from the tools you must mention it explicitly without mentioning the tool names - I have forgotten all TOOL RESPONSES! Remember to respond with a markdown code snippet of a json blob with a single action, and NOTHING else - even if you just want to respond to the user. Do NOT respond with anything except a JSON snippet no matter what!\""
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.agents.json_chat.prompt import TEMPLATE_TOOL_RESPONSE\n",
    "\n",
    "TEMPLATE_TOOL_RESPONSE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "0d37af86",
   "metadata": {},
   "outputs": [],
   "source": [
    "TEMPLATE_TOOL_RESPONSE = \"\"\"工具响应：\n",
    "---------------------\n",
    "{observation}\n",
    "\n",
    "用户的输入：\n",
    "---------------------\n",
    "请根据工具的响应判断，是否能够回答问题：\n",
    "\n",
    "{input}\n",
    "\n",
    "请根据工具响应的内容，思考接下来回复。回复格式严格按照前面所说的2种JSON回复格式，选择其中1种进行回复。请记住通过单个选项，以JSON模式格式化的回复内容，不要回复其他内容。\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "18aa7260",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage\n",
    "def format_log_to_messages(\n",
    "    query,\n",
    "    intermediate_steps,\n",
    "    template_tool_response,\n",
    "):\n",
    "    \"\"\"Construct the scratchpad that lets the agent continue its thought process.\"\"\"\n",
    "    thoughts: List[BaseMessage] = []\n",
    "    for action, observation in intermediate_steps:\n",
    "        thoughts.append(AIMessage(content=action.log))\n",
    "        human_message = HumanMessage(\n",
    "            content=template_tool_response.format(input=query,observation=observation)\n",
    "        )\n",
    "        thoughts.append(human_message)\n",
    "    return thoughts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "94c5b783",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents.agent import AgentOutputParser\n",
    "from langchain_core.output_parsers.json import parse_json_markdown\n",
    "from langchain_core.exceptions import OutputParserException\n",
    "from langchain_core.agents import AgentAction, AgentFinish\n",
    "class JSONAgentOutputParser(AgentOutputParser):\n",
    "    \"\"\"Parses tool invocations and final answers in JSON format.\n",
    "\n",
    "    Expects output to be in one of two formats.\n",
    "\n",
    "    If the output signals that an action should be taken,\n",
    "    should be in the below format. This will result in an AgentAction\n",
    "    being returned.\n",
    "\n",
    "    ```\n",
    "    {\n",
    "      \"action\": \"search\",\n",
    "      \"action_input\": \"2+2\"\n",
    "    }\n",
    "    ```\n",
    "\n",
    "    If the output signals that a final answer should be given,\n",
    "    should be in the below format. This will result in an AgentFinish\n",
    "    being returned.\n",
    "\n",
    "    ```\n",
    "    {\n",
    "      \"action\": \"Final Answer\",\n",
    "      \"answer\": \"4\"\n",
    "    }\n",
    "    ```\n",
    "    \"\"\"\n",
    "\n",
    "    def parse(self, text):\n",
    "        try:\n",
    "            response = parse_json_markdown(text)\n",
    "            if isinstance(response, list):\n",
    "                # gpt turbo frequently ignores the directive to emit a single action\n",
    "                logger.warning(\"Got multiple action responses: %s\", response)\n",
    "                response = response[0]\n",
    "            if response[\"action\"] == \"Final Answer\":\n",
    "                return AgentFinish({\"output\": response[\"answer\"]}, text)\n",
    "            else:\n",
    "                return AgentAction(\n",
    "                    response[\"action\"], response.get(\"action_input\", {}), text\n",
    "                )\n",
    "        except Exception as e:\n",
    "            raise OutputParserException(f\"Could not parse LLM output: {text}\") from e\n",
    "\n",
    "    @property\n",
    "    def _type(self) -> str:\n",
    "        return \"json-agent\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "cc6392cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# from langchain.agents.output_parsers import JSONAgentOutputParser\n",
    "from langchain_core.runnables import Runnable, RunnablePassthrough\n",
    "\n",
    "\n",
    "agent = (\n",
    "        RunnablePassthrough.assign(\n",
    "            agent_scratchpad=lambda x: format_log_to_messages(\n",
    "                x[\"input\"],\n",
    "                x[\"intermediate_steps\"], \n",
    "                template_tool_response=TEMPLATE_TOOL_RESPONSE\n",
    "            )\n",
    "        )\n",
    "        | prompt\n",
    "        | llm\n",
    "        | JSONAgentOutputParser()\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "c178fe4a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents import AgentExecutor\n",
    "\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "02214206",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m```json\n",
      "{ \n",
      "   \"action\": \"run_shell\", \n",
      "   \"action_input\": \"mkdir 老陈\" \n",
      "}\n",
      "```\u001b[0mExecuting command:\n",
      " ['mkdir 老陈']\n",
      "\u001b[38;5;200m\u001b[1;3m\u001b[0m"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\a4154\\AppData\\Roaming\\Python\\Python310\\site-packages\\langchain_community\\tools\\shell\\tool.py:32: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32;1m\u001b[1;3m```json\n",
      "{ \n",
      "    \"action\": \"Final Answer\", \n",
      "    \"answer\": \"一个名为'老陈'的文件夹已经在当前目录下创建成功了。\"\n",
      "}\n",
      "```\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '当前目录创建1个老陈文件夹', 'output': \"一个名为'老陈'的文件夹已经在当前目录下创建成功了。\"}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"当前目录创建1个老陈文件夹\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "20990ab3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m```json\n",
      "{ \n",
      "   \"action\": \"run_shell\", \n",
      "   \"action_input\": \"start chrome http://www.baidu.com/s?wd=黑悟空\" \n",
      "}\n",
      "```\u001b[0mExecuting command:\n",
      " ['start chrome http://www.baidu.com/s?wd=黑悟空']\n",
      "\u001b[38;5;200m\u001b[1;3m\u001b[0m"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\a4154\\AppData\\Roaming\\Python\\Python310\\site-packages\\langchain_community\\tools\\shell\\tool.py:32: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32;1m\u001b[1;3m```json\n",
      "{ \n",
      "    \"action\": \"Final Answer\", \n",
      "    \"answer\": \"浏览器已打开并搜索了'黑悟空'的相关信息。\" \n",
      "}\n",
      "```\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '打开浏览器使用百度搜索黑悟空', 'output': \"浏览器已打开并搜索了'黑悟空'的相关信息。\"}"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"打开浏览器使用百度搜索黑悟空\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68f909cf",
   "metadata": {},
   "source": [
    "## 添加历史记录\n",
    "\n",
    "这太棒了 - 我们有一个代理！但是，此代理是无状态的 - 它不记得有关先前交互的任何信息。这意味着你不能轻易地提出后续问题。让我们通过添加内存来解决这个问题。\n",
    "\n",
    "为此，我们需要做两件事：\n",
    "\n",
    "1. 在提示符中为内存变量添加一个位置\n",
    "2. 跟踪聊天记录\n",
    "\n",
    "首先，让我们在提示中添加一个内存位置。为此，我们通过为带有 键 \"chat_history\" 的消息添加一个占位符 .请注意，我们将其放在新用户输入的上方（以遵循对话流）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 192,
   "id": "f9745cf7",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import MessagesPlaceholder\n",
    "\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"你是非常强大的助手，你可以使用各种工具来完成人类交给的问题和任何。\",\n",
    "        ),\n",
    "        MessagesPlaceholder(variable_name=\"chat_history\"),\n",
    "        (\"user\", promptTemplate),\n",
    "        MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 193,
   "id": "11e38964",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import AIMessage, HumanMessage\n",
    "\n",
    "chat_history = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "id": "3a565edc",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = prompt.partial(\n",
    "        tools=render_text_description(list(tools)),\n",
    "        tool_names=\", \".join([t.name for t in tools]),\n",
    ")\n",
    "\n",
    "agent = (\n",
    "        RunnablePassthrough.assign(\n",
    "            agent_scratchpad=lambda x: format_log_to_messages(\n",
    "                x[\"input\"],\n",
    "                x[\"intermediate_steps\"], \n",
    "                template_tool_response=TEMPLATE_TOOL_RESPONSE,\n",
    "            )\n",
    "        )\n",
    "        | prompt\n",
    "        | llm\n",
    "        | JSONAgentOutputParser()\n",
    "    )\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 197,
   "id": "0b13f36d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m{\n",
      "  \"action\": \"Final Answer\",\n",
      "  \"answer\": \"你的名字是老陈。\"\n",
      "}\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '我叫什么名字？',\n",
       " 'chat_history': [HumanMessage(content='你好，我是老陈'),\n",
       "  AIMessage(content='你好，老陈。很高兴认识你！'),\n",
       "  HumanMessage(content='你好，我是老陈'),\n",
       "  AIMessage(content='你好，老陈。很高兴认识你！')],\n",
       " 'output': '你的名字是老陈。'}"
      ]
     },
     "execution_count": 197,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "\n",
    "chat_history.extend(\n",
    "    [\n",
    "        HumanMessage(content=\"你好，我是老陈\"),\n",
    "        AIMessage(content=\"你好，老陈。很高兴认识你！\"),\n",
    "    ]\n",
    ")\n",
    "agent_executor.invoke({\"input\": \"我叫什么名字？\", \"chat_history\": chat_history})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 199,
   "id": "10d9b189",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m{\n",
      "  \"action\": \"Final Answer\",\n",
      "  \"answer\": \"是的，朱丽倩曾出演过电影。她曾在1984年上映的马来西亚电影《神雕侠侣》中饰演小龙女一角。\"\n",
      "}\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '刘德华老婆有演过电影吗',\n",
       " 'chat_history': [HumanMessage(content='你好，我是老陈'),\n",
       "  AIMessage(content='你好，老陈。很高兴认识你！'),\n",
       "  HumanMessage(content='你好，我是老陈'),\n",
       "  AIMessage(content='你好，老陈。很高兴认识你！'),\n",
       "  HumanMessage(content='刘德华的老婆是谁？'),\n",
       "  AIMessage(content='刘德华的老婆是朱丽倩（Carol Chu）。')],\n",
       " 'output': '是的，朱丽倩曾出演过电影。她曾在1984年上映的马来西亚电影《神雕侠侣》中饰演小龙女一角。'}"
      ]
     },
     "execution_count": 199,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat_history.extend(\n",
    "    [\n",
    "        HumanMessage(content=\"刘德华的老婆是谁？\"),\n",
    "        AIMessage(content=\"刘德华的老婆是朱丽倩（Carol Chu）。\"),\n",
    "    ]\n",
    ")\n",
    "agent_executor.invoke({\"input\": \"刘德华老婆有演过电影吗\", \"chat_history\": chat_history})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1a347a6",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
